package com.qq.aop;

import com.qq.anonatation.LogAnotation;
import com.qq.anonatation.Transaction;
import com.qq.manager.SimpleTxManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class TransactionAspect {
    private final SimpleTxManager txManager;

    public TransactionAspect(SimpleTxManager txManager) {
        this.txManager = txManager;
    }

    @Around("@annotation(transaction) || @within(transaction)")
    public Object around(ProceedingJoinPoint pjp, Transaction transaction) throws Throwable {
        System.out.println("事务开始");
        //从方法上、从类上去获取到 Transaction 注解
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        Class<?> targetClass = pjp.getTarget().getClass();
        Transaction tx = method.getAnnotation(Transaction.class);
        if (tx == null) {
            tx = targetClass.getAnnotation(Transaction.class);
        }

        //判断是否是新事务，是新事务则开一个事务
        boolean isNew = (SimpleTxManager.currentConnection() == null);
        if (isNew) txManager.begin(tx != null && tx.readOnly());

        try {
            Object result = pjp.proceed();
            if (isNew) txManager.commit();
            System.out.println("事务提交成功");
            return result;
        } catch (Throwable ex) {
            if (isNew && shouldRollback(tx, ex)) {
                System.out.println("事务回滚");
                txManager.rollback();
            } else if (isNew) {
                System.out.println("事务提交");
                txManager.commit();
            }
            throw ex;
        }
    }

    private boolean shouldRollback(Transaction tx, Throwable ex) {
        if (tx == null) return (ex instanceof RuntimeException || ex instanceof Error);

        for (Class<? extends Throwable> ignoreEx : tx.noRollbackFor()) {
            if (ignoreEx.isAssignableFrom(ex.getClass())) return false;
        }

        for (Class<? extends Throwable> rollbackEx : tx.rollbackFor()) {
            if (rollbackEx.isAssignableFrom(ex.getClass())) return true;
        }

        return (ex instanceof RuntimeException || ex instanceof Error);
    }
}
